package Service

import (
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"math/big"
	"strings"

	"log"
	"net/http"
	"sync"

	"github.com/ant0ine/go-json-rest/rest"

	"context"
	"crypto/ecdsa"

	token "gitee.com/freebird92/wallet-service/mycontract"
	"github.com/ethereum/go-ethereum"
	"github.com/ethereum/go-ethereum/accounts/abi/bind"
	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/common/hexutil"
	"github.com/ethereum/go-ethereum/core/types"
	"github.com/ethereum/go-ethereum/crypto"
	"github.com/ethereum/go-ethereum/crypto/sha3"
	"github.com/ethereum/go-ethereum/ethclient"
)

const (
	AddressLength = 20
)

type Auth struct {
	Phone    string `json:"phone"`
	PassWord string `json:"password"`
}

type PostOk struct {
	Ok string `json:"ok"`
}

type ReqSendTx struct {
	PrivateKey   string   `json:"privatekey"`
	AccountNonce uint64   `json:"nonce"    `
	Price        *big.Int `json:"gasprice" `
	GasLimit     uint64   `json:"gaslimit"      `
	Recipient    string   `json:"to"       `
	Amount       float64  `json:"value"    `
	Payload      string   `json:"data"    `
	value        *big.Int
}

type ReqGetBalance struct {
	Address string  `json:"address"`
	Balance float64 `json:"balance"`
}

/*
# 用户注册
curl -H 'Content-Type: application/json' -d '{"auth":{"phone":"15199998888", "password":"abc"},"phone":"15199998888", "password":"abc", "company":"abc", "type":"driver_type_1"}' http://120.76.47.102:7123/api/register
# 用户登录
curl -H 'Content-Type: application/json' -d '{"auth":{"phone":"15199998888", "password":"abc"},"phone":"15199998888", "password":"abc", "type":"type_1"}' http://120.76.47.102:7123/api/login
# 改用户密码
*/

var mMaxId int32

var lock = sync.RWMutex{}
var RpcUrl = ""

func Init(rpc, dsn, port string) error {
	RpcUrl = rpc
	api := rest.NewApi()
	api.Use(rest.DefaultDevStack...)
	router, err := rest.MakeRouter(

		rest.Post("/api/sendtx", SendTx),
		rest.Post("/api/getbalance", GetBalance),
		rest.Post("/api/sendtokentx", SendTokenTx),
		rest.Post("/api/getbalancetoken", GetBalanceToken),
	)

	if err != nil {
		log.Fatal(err)
	}
	api.SetApp(router)
	log.Fatal(http.ListenAndServe(":"+port, api.MakeHandler()))

	return nil
}

var (
	// ErrJsonPayloadEmpty is returned when the JSON payload is empty.
	ErrJsonPayloadEmpty = errors.New("JSON payload is empty")
)

func DecodeJsonPayload(r *rest.Request, v interface{}) error {
	content, err := ioutil.ReadAll(r.Body)
	fmt.Println(string(content))
	r.Body.Close()
	if err != nil {
		return err
	}
	if len(content) == 0 {
		return ErrJsonPayloadEmpty
	}
	err = json.Unmarshal(content, v)
	if err != nil {
		return err
	}
	return nil
}

func EtherToWei(val float64) *big.Int {
	bigval := new(big.Float)
	bigval.SetFloat64(val)
	// Set precision if required.
	// bigval.SetPrec(64)

	coin := new(big.Float)
	coin.SetInt(big.NewInt(1000000000000000000))

	bigval.Mul(bigval, coin)

	result := new(big.Int)
	bigval.Int(result) // store converted number in result

	return result
}
func WeiToEther(bigint *big.Int) float64 {
	bigval := new(big.Float)
	bigval.SetInt(bigint)
	coin := new(big.Float)
	coin.SetFloat64(0.000000000000000001)

	bigval.Mul(bigval, coin)
	val, _ := bigval.Float64()
	return val
}

func SendTx(w rest.ResponseWriter, r *rest.Request) {
	defer func() { // 必须要先声明defer，否则不能捕获到panic异常
		if err := recover(); err != nil {
			fmt.Println("recovering::", err) // 这里的err其实就是panic传入的内容，55
		}
	}()

	tx := &ReqSendTx{}
	err := DecodeJsonPayload(r, &tx)
	if err != nil {
		rest.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	err = SendTxImp(tx)
	if err != nil {
		rest.Error(w, err.Error(), 400)
		return
	}
	w.WriteJson(&tx)
}

func SendTxImp(txReq *ReqSendTx) error {
	client, err := ethclient.Dial(RpcUrl)
	if err != nil {
		log.Println(err)
		return err
	}

	privateKey, err := crypto.HexToECDSA(txReq.PrivateKey)
	if err != nil {
		log.Println(err)
		return err
	}

	publicKey := privateKey.Public()
	publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey)
	if !ok {
		log.Println("error casting public key to ECDSA")
		return err
	}

	fromAddress := crypto.PubkeyToAddress(*publicKeyECDSA)
	nonce, err := client.PendingNonceAt(context.Background(), fromAddress)
	if err != nil {
		log.Println(err)
		return err
	}

	gasPrice, err := client.SuggestGasPrice(context.Background())
	if err != nil {
		log.Println(err)
	}

	// 更新nonce , gasPrice
	txReq.AccountNonce = nonce
	txReq.Price = gasPrice

	toAddress := common.HexToAddress(txReq.Recipient)
	txReq.Payload = strings.ToLower(txReq.Payload)
	txReq.Payload = strings.Replace(txReq.Payload, "0x", "", 1)
	data, err := hex.DecodeString(txReq.Payload)
	txReq.value = EtherToWei(txReq.Amount)

	tx := types.NewTransaction(txReq.AccountNonce, toAddress, txReq.value, txReq.GasLimit, txReq.Price, data)

	chainID, err := client.NetworkID(context.Background())
	if err != nil {
		log.Println(err)
	}

	fmt.Println("before signedTx:", txReq, toAddress, data)
	signedTx, err := types.SignTx(tx, types.NewEIP155Signer(chainID), privateKey)
	if err != nil {
		log.Println(err)
	}
	fmt.Println("signedTx:", signedTx)
	err = client.SendTransaction(context.Background(), signedTx)
	if err != nil {
		log.Println(err)
		return err
	}

	fmt.Printf("tx sent: %s", signedTx.Hash().Hex())
	return nil
}

func GetBalance(w rest.ResponseWriter, r *rest.Request) {
	defer func() { // 必须要先声明defer，否则不能捕获到panic异常
		if err := recover(); err != nil {
			fmt.Println("recovering::", err) // 这里的err其实就是panic传入的内容，55
		}
	}()

	data := &ReqGetBalance{}
	err := DecodeJsonPayload(r, &data)
	if err != nil {
		rest.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	err = GetBalanceImp(data)
	if err != nil {
		rest.Error(w, err.Error(), 400)
		return
	}
	w.WriteJson(&data)
}

func GetBalanceImp(reqBalance *ReqGetBalance) error {
	client, err := ethclient.Dial(RpcUrl)
	if err != nil {
		log.Println(err)
		return err
	}
	account := common.HexToAddress(reqBalance.Address)
	balance, err := client.BalanceAt(context.Background(), account, nil)
	if err != nil {
		log.Println(err)
		return err
	}
	reqBalance.Balance = WeiToEther(balance)
	return nil
}

func GetBalanceToken(w rest.ResponseWriter, r *rest.Request) {
	defer func() { // 必须要先声明defer，否则不能捕获到panic异常
		if err := recover(); err != nil {
			fmt.Println("recovering::", err) // 这里的err其实就是panic传入的内容，55
		}
	}()

	data := &ReqGetBalance{}
	err := DecodeJsonPayload(r, &data)
	if err != nil {
		rest.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	err = GetBalanceTokenImp(data)
	if err != nil {
		rest.Error(w, err.Error(), 400)
		return
	}
	w.WriteJson(&data)
}

func GetBalanceTokenImp(reqBalance *ReqGetBalance) error {
	client, err := ethclient.Dial(RpcUrl)
	if err != nil {
		log.Println(err)
		return err
	}
	// 取得TC地址
	tokenAddress := common.HexToAddress("4f8620c8e736a5e9e7653743b6060e3fddc3a070")
	instance, err := token.NewToken(tokenAddress, client)
	if err != nil {
		log.Println(err)
		return err
	}

	address := common.HexToAddress(reqBalance.Address)
	balance, err := instance.BalanceOf(&bind.CallOpts{}, address)
	if err != nil {
		log.Println(err)
		return err
	}

	reqBalance.Balance = WeiToEther(balance)
	return nil
}

func SendTokenTx(w rest.ResponseWriter, r *rest.Request) {
	defer func() { // 必须要先声明defer，否则不能捕获到panic异常
		if err := recover(); err != nil {
			fmt.Println("recovering::", err) // 这里的err其实就是panic传入的内容，55
		}
	}()

	tx := &ReqSendTx{}
	err := DecodeJsonPayload(r, &tx)
	if err != nil {
		rest.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	err = SendTokenTxImp(tx)
	if err != nil {
		rest.Error(w, err.Error(), 400)
		return
	}
	w.WriteJson(&tx)
}

func SendTokenTxImp(txReq *ReqSendTx) error {
	client, err := ethclient.Dial(RpcUrl)
	if err != nil {
		log.Println(err)
		return err
	}
	privateKey, err := crypto.HexToECDSA(txReq.PrivateKey)
	if err != nil {
		log.Println(err)
		return err
	}

	publicKey := privateKey.Public()
	publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey)
	if !ok {
		log.Println("error casting public key to ECDSA")
		return err
	}
	fromAddress := crypto.PubkeyToAddress(*publicKeyECDSA)
	nonce, err := client.PendingNonceAt(context.Background(), fromAddress)
	if err != nil {
		log.Println(err)
		return err
	}

	value := big.NewInt(0) // in wei (0 eth)
	gasPrice, err := client.SuggestGasPrice(context.Background())
	if err != nil {
		log.Fatal(err)
	}

	// gasPrice, err := client.SuggestGasPrice(context.Background())
	// if err != nil {
	// 	log.Println(err)
	// }

	toAddress := common.HexToAddress(txReq.Recipient)
	// 取得TC地址
	tokenAddress := common.HexToAddress("4f8620c8e736a5e9e7653743b6060e3fddc3a070")

	transferFnSignature := []byte("transfer(address,uint256)")
	hash := sha3.NewKeccak256()
	hash.Write(transferFnSignature)
	methodID := hash.Sum(nil)[:4]
	fmt.Println(hexutil.Encode(methodID)) // 0xa9059cbb

	paddedAddress := common.LeftPadBytes(toAddress.Bytes(), 32)
	fmt.Println(hexutil.Encode(paddedAddress))

	amount := new(big.Int)
	amount.SetString("1000000000000000000000", 10) // 1000 tokens
	paddedAmount := common.LeftPadBytes(amount.Bytes(), 32)
	fmt.Println(hexutil.Encode(paddedAmount)) // 0x00000000000000000000000000000000000000000000003635c9adc5dea00000

	var data []byte
	data = append(data, methodID...)
	data = append(data, paddedAddress...)
	data = append(data, paddedAmount...)

	gasLimit, err := client.EstimateGas(context.Background(), ethereum.CallMsg{
		To:   &tokenAddress,
		Data: data,
	})
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(gasLimit) // 23256

	tx := types.NewTransaction(nonce, tokenAddress, value, gasLimit, gasPrice, data)
	chainID, err := client.NetworkID(context.Background())
	if err != nil {
		log.Fatal(err)
	}

	signedTx, err := types.SignTx(tx, types.NewEIP155Signer(chainID), privateKey)
	if err != nil {
		log.Fatal(err)
	}

	err = client.SendTransaction(context.Background(), signedTx)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("tx sent: %s", signedTx.Hash().Hex()) // tx sent: 0xa56316b637a94c4cc0331c73ef26389d6c097506d581073f927275e7a6ece0bc

	return nil
}
